package org.zoomdev.zoom;

import org.codehaus.jackson.JsonEncoding;
import org.codehaus.jackson.JsonFactory;
import org.codehaus.jackson.JsonGenerator;
import org.zoomdev.zoom.aop.Getter;
import org.zoomdev.zoom.aop.factory.GetterFactory;
import org.zoomdev.zoom.common.exceptions.ZoomException;
import org.zoomdev.zoom.common.io.Io;
import org.zoomdev.zoom.common.utils.Classes;

import java.io.IOException;
import java.io.OutputStream;
import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;

public final class JsonWriter {
    static final JsonFactory jsonFactory = FactoryHolder.jsonFactory;
    private static final Map<Type, ValueWriter> writerCache = new ConcurrentHashMap<>();

    // 基础类型处理器注册
    static {
        writerCache.put(String.class, new StringWriter());
        writerCache.put(Integer.class, new IntWriter());
        writerCache.put(Integer.TYPE, new IntWriter());
        writerCache.put(Long.class, new LongWriter());
        writerCache.put(Long.TYPE, new LongWriter());
        writerCache.put(Boolean.class, new BooleanWriter());
        writerCache.put(Boolean.TYPE, new BooleanWriter());
        writerCache.put(Double.class, new DoubleWriter());
        writerCache.put(Double.TYPE, new DoubleWriter());
        writerCache.put(UUID.class, new UUIDToStringWriter());
    }

    public static void write(OutputStream out, ValueWriter writer, Object value) throws IOException {
        JsonGenerator jg = jsonFactory.createJsonGenerator(out, JsonEncoding.UTF8);
        try {
            if (value == null) {
                jg.writeNull();
                return;
            }
            writer.write(jg, value);
        } finally {
            Io.close(jg);
        }
    }

    public static void write(OutputStream out, Object value) throws IOException {
        JsonGenerator jg = jsonFactory.createJsonGenerator(out, JsonEncoding.UTF8);
        try {
            if (value == null) {
                jg.writeNull();
                return;
            }
            getValueWriter(value.getClass()).write(jg, value);
        } finally {
            Io.close(jg);
        }
    }

    public static ValueWriter getValueWriter(Type type) {
        return writerCache.computeIfAbsent(type, t -> {
            if (t instanceof Class) {
                Class clazz = (Class) t;
                if (clazz.isArray()) {
                    return new ArrayWriter(clazz.getComponentType());
                }
                if (List.class.isAssignableFrom(clazz)) {
                    return new ListWriter(getValueWriter(Object.class));
                }
                if (Map.class.isAssignableFrom(clazz)) {
                    return new MapWriter(Object.class, Object.class);
                }
                if (clazz.isEnum()) {
                    return new EnumWriter(clazz);
                }
                return createBeanValueWriter(clazz);
            } else if (t instanceof ParameterizedType) {
                ParameterizedType pt = (ParameterizedType) t;
                Type rawType = pt.getRawType();
                if (rawType == List.class) {
                    Type elementType = pt.getActualTypeArguments()[0];
                    return new ListWriter(getValueWriter(elementType));
                }
                if (rawType == Map.class) {
                    Type[] typeArgs = pt.getActualTypeArguments();
                    return new MapWriter(typeArgs[0], typeArgs[1]);
                }
            }
            throw new ZoomException("不支持的类型" + t);
        });
    }


    private static BeanValueWriter createBeanValueWriter(Class clazz) {
        List<Field> fields = Classes.getFields(clazz);
        PropertyWriter[] writers = new PropertyWriter[fields.size()];
        for (int i = 0, c = fields.size(); i < c; ++i) {
            Field f = fields.get(i);
            Getter getter = GetterFactory.FACTORY.create(f);
            PropertyWriter writer = new PropertyWriter(f.getName(), getter, getValueWriter(f.getGenericType()));
            writers[i] = writer;
        }
        return new BeanValueWriter(writers);
    }

    static final class PropertyWriter {
        final String name;
        final Getter getter;
        final ValueWriter writer;

        PropertyWriter(String name, Getter getter, ValueWriter writer) {
            this.name = name;
            this.getter = getter;
            this.writer = writer;
        }

        void write(JsonGenerator jg, Object data) throws IOException {
            jg.writeFieldName(name);
            Object value = getter.get(data);
            if (value == null) {
                jg.writeNull();
            } else {
                writer.write(jg, value);
            }
        }

    }

    public static abstract class ValueWriter<T> {
        abstract void write(JsonGenerator jg, T value) throws IOException;
    }

    static class BeanValueWriter extends ValueWriter {

        private final PropertyWriter[] writers;

        public BeanValueWriter(PropertyWriter[] writers) {
            super();
            this.writers = writers;
        }

        void write(JsonGenerator jg, Object data) throws IOException {
            jg.writeStartObject();
            final PropertyWriter[] writers = this.writers;
            for (int i = 0, c = this.writers.length; i < c; ++i) {
                PropertyWriter writer = writers[i];
                writer.write(jg, data);
            }
            jg.writeEndObject();
        }
    }


    // 基础类型处理器实现
    static class StringWriter extends ValueWriter<String> {
        @Override
        void write(JsonGenerator jg, String value) throws IOException {
            jg.writeString(value);
        }
    }

    static class IntWriter extends ValueWriter<Integer> {
        @Override
        void write(JsonGenerator jg, Integer value) throws IOException {
            jg.writeNumber(value);
        }
    }

    static class LongWriter extends ValueWriter<Long> {
        @Override
        void write(JsonGenerator jg, Long value) throws IOException {
            jg.writeNumber(value);
        }
    }

    static class BooleanWriter extends ValueWriter<Boolean> {
        @Override
        void write(JsonGenerator jg, Boolean value) throws IOException {
            jg.writeBoolean(value);
        }
    }

    static class DoubleWriter extends ValueWriter<Double> {
        @Override
        void write(JsonGenerator jg, Double value) throws IOException {
            jg.writeNumber(value);
        }
    }

    static class UUIDToStringWriter extends ValueWriter<UUID> {
        @Override
        void write(JsonGenerator jg, UUID value) throws IOException {
            jg.writeString(value.toString());
        }
    }


    // 集合类型处理器
    static class ListWriter extends ValueWriter {
        private final ValueWriter elementWriter;

        ListWriter(ValueWriter elementWriter) {
            this.elementWriter = elementWriter;
        }

        @Override
        void write(JsonGenerator jg, Object value) throws IOException {
            List<?> list = (List<?>) value;
            jg.writeStartArray();
            for (int i = 0, c = list.size(); i < c; ++i) {
                elementWriter.write(jg, list.get(i));
            }
            jg.writeEndArray();
        }
    }

    static class MapWriter extends ValueWriter {
        private final Type keyType;
        private final Type valueType;
        private ValueWriter keyWriter;
        private ValueWriter valueWriter;

        MapWriter(Type keyType, Type valueType) {
            this.keyType = keyType;
            this.valueType = valueType;
        }

        @Override
        void write(JsonGenerator jg, Object value) throws IOException {
            Map<?, ?> map = (Map<?, ?>) value;
            jg.writeStartObject();
            for (Map.Entry<?, ?> entry : map.entrySet()) {
                if (keyWriter == null) keyWriter = getValueWriter(keyType);
                if (valueWriter == null) valueWriter = getValueWriter(valueType);

                jg.writeFieldName(entry.getKey().toString());
                valueWriter.write(jg, entry.getValue());
            }
            jg.writeEndObject();
        }
    }

    // 其他类型处理器
    static class ArrayWriter extends ValueWriter {
        private final Class<?> componentType;
        private ValueWriter elementWriter;

        ArrayWriter(Class<?> componentType) {
            this.componentType = componentType;
        }

        @Override
        void write(JsonGenerator jg, Object value) throws IOException {
            Object[] array = (Object[]) value;
            jg.writeStartArray();
            for (Object item : array) {
                if (elementWriter == null) {
                    elementWriter = getValueWriter(componentType);
                }
                elementWriter.write(jg, item);
            }
            jg.writeEndArray();
        }
    }

    static class EnumWriter extends ValueWriter {
        private final Class<? extends Enum> enumType;

        EnumWriter(Class<?> enumType) {
            this.enumType = (Class<? extends Enum>) enumType;
        }

        @Override
        void write(JsonGenerator jg, Object value) throws IOException {
            jg.writeString(((Enum) value).name());
        }
    }
}
